package job.tracker.controller

import grails.validation.ValidationException;
import job.tracker.controller.JobSiteCommand;
import job.tracker.domain.*
import job.tracker.service.*;

class JobSiteController {

	static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]

	JobService jobService
	UtilService utilService
	def grailsApplication

	def beforeInterceptor = [action:this.&authorize]

	def authorize() {
		if (!session.user) {
			withFormat {
				json {
					render(contentType: "text/json", status: 401) { errorMessage = 'access not allowed. Log in first' }
				}
			}
			return false
		}
	}

	def refreshJobSites = {
		User user = User.get(session.user.id)

		Date now = new Date();
		//FIXME: remember to use config time
		int refreshIntervalHours = grailsApplication.config.jobTracker.refreshIntervalHours

		List<JobSite> approvedSites = user.jobSites.findAll {
			if (it.approvalStatus == JobSiteApprovalStatusEnum.APPROVED) {
				if (!it.lastSyncDateTime) {
					return true;
				}

				Calendar cal = Calendar.instance;
				cal.setTime(it.lastSyncDateTime);
				cal.add(Calendar.MINUTE, refreshIntervalHours);
				//cal.add(Calendar.HOUR, refreshIntervalHours);

				return now.after(cal.getTime())
			}
			return false;
		}

		List<JobSite> changedSites = []
		for (JobSite jobSite : approvedSites) {
			List<String> fetchedJobs = []
			try {
				fetchedJobs = utilService.scrapeJobs(jobSite);
			}
			catch(Exception e) {
				//time out most likely happened
				//TODO: log & email admin
				println e
				continue;
			}

			jobSite.lastSyncDateTime = new Date();

			List<String> savedJobs = jobSite.jobs*.content;
			List<String> commonJobs = savedJobs.intersect(fetchedJobs);
			List<String> dbDelta = savedJobs - commonJobs

			List<Job> removeFromDB = jobSite.jobs.findAll { Job job -> dbDelta.contains(job.content) }
			List<Job> addToDB = (fetchedJobs - commonJobs).collect { new Job(content: it) }

			if (addToDB || removeFromDB) {
				jobSite.pageLastChanged = jobSite.lastSyncDateTime
				changedSites.add([
							jobSite: jobSite,
							added : addToDB.clone(),
							removed : removeFromDB.clone()
						]);
			}

			jobService.syncronizeJobs(jobSite, addToDB, removeFromDB);

		}



		withFormat {
			json {
				render(contentType: "text/json", status: 200) {
					array {
						changedSites.each { changedSite ->
							jobSite(
									id : changedSite.jobSite.id,
									pageLastChanged : changedSite.jobSite.pageLastChanged,
									added : array {
										changedSite.added.each { Job job -> jbA(id: job.id, content: job.content) }
									},
									removed : array {
										changedSite.removed.each { Job job -> jbR(id: job.id, content: job.content) }
									}
									)
						}
					}
				}
			}
		}
	}

	def list = {
		User user = User.get(session.user.id)

		List<JobSite> jobsites = (user.admin) ? JobSite.list() : user.jobSites;

		withFormat {
			json {
				render(contentType: "text/json", status: 200) {
					array {
						jobsites.each {
							jobSite(
									id : it.id,
									pageUrl: it.pageUrl,
									dataElement: it.dataElement,
									approvalStatus: it.approvalStatus.name(),
									name: it.name,
									description: it.description,
									pageLastChanged : it.pageLastChanged
									)
						}
					}
				}
			}
		}
	}

	def save = { JobSiteCommand cmd ->
		if (!cmd.validate()) {
			withFormat {
				json {
					render(contentType: "text/json", status: 500) {
						fieldErrors = array {
							cmd.errors.fieldErrors.each {
								err(defaultMessage: it.defaultMessage, field: it.field, rejectedValue: it.rejectedValue)
							}
						}
					}
				}
			}
			return
		}


		JobSite jobSite = JobSite.findByPageUrl(cmd.pageUrl) ?: new JobSite(
				pageUrl: cmd.pageUrl,
				name: cmd.name,
				description: cmd.description
				);

		User user = User.get(session.user.id)

		if (user.jobSites.contains(jobSite)) {
			withFormat {
				json {
					render(contentType: "text/json", status: 500) { errorMessage = 'Duplicate entry' }
				}
			}
			return
		}

		try {
			jobService.saveJobSite(user, jobSite);
			withFormat {
				json {
					render(contentType: "text/json", status: 200)
				}
			}
		}
		catch (ValidationException e) {
			withFormat {
				json {
					render(contentType: "text/json", status: 500) {
						fieldErrors = array {
							e.errors.fieldErrors.each {
								err(defaultMessage: it.defaultMessage, field: it.field, rejectedValue: it.rejectedValue)
							}
						}
					}
				}
			}
		}
		catch (RuntimeException e) {
			withFormat {
				json {
					render(contentType: "text/json", status: 500) { errorMessage = e.message }
				}
			}
		}
	}

	def show = {
		def jobSiteInstance = JobSite.get(params.id)
		if (!jobSiteInstance) {
			flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'jobSite.label', default: 'JobSite'), params.id])}"
			redirect(action: "list")
		}
		else {
			[jobSiteInstance: jobSiteInstance]
		}
	}

	def update = { JobSiteCommand cmd ->
		JobSite jobSite = JobSite.get(cmd.id)
		if (!jobSite) {
			withFormat {
				json {
					render(contentType: "text/json", status: 404) {  errorMessage = 'Not found' }
				}
			}
			return
		}

		if (jobSite.approvalStatus != JobSiteApprovalStatusEnum.PENDING && !session.user.admin) {
			withFormat {
				json {
					render(contentType: "text/json", status: 401) {  errorMessage = 'Reviewd already. Cant be edited' }
				}
			}
			return
		}

		if (!cmd.validate()) {
			withFormat {
				json {
					render(contentType: "text/json", status: 500) {
						fieldErrors = array {
							cmd.errors.fieldErrors.each {
								err(defaultMessage: it.defaultMessage, field: it.field, rejectedValue: it.rejectedValue)
							}
						}
					}
				}
			}
			return
		}

		jobSite.name = cmd.name
		jobSite.pageUrl = cmd.pageUrl
		jobSite.description = cmd.description

		if (session.user.admin) {
			jobSite.approvalStatus = cmd.approvalStatus as JobSiteApprovalStatusEnum
			jobSite.dataElement = cmd.dataElement
		}

		try {
			jobSite.save()
			withFormat {
				json {
					render(contentType: "text/json", status: 200)
				}
			}
		}
		catch (ValidationException e) {
			withFormat {
				json {
					render(contentType: "text/json", status: 500) {
						fieldErrors = array {
							e.errors.allErrors.each {
								err(defaultMessage: it.defaultMessage, field: it.field, rejectedValue: it.rejectedValue)
							}
						}
					}
				}
			}
		}
		catch (RuntimeException e) {
			withFormat {
				json {
					render(contentType: "text/json", status: 500) { errorMessage = e.message }
				}
			}
		}
	}

	def delete = {
		def jobSiteInstance = JobSite.get(params.id)
		if (jobSiteInstance) {
			try {
				jobSiteInstance.delete(flush: true)
				flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'jobSite.label', default: 'JobSite'), params.id])}"
				println "here"
				//redirect(action: "list")
			}
			catch (org.springframework.dao.DataIntegrityViolationException e) {
				flash.message = "${message(code: 'default.not.deleted.message', args: [message(code: 'jobSite.label', default: 'JobSite'), params.id])}"
				println "here2"
				//redirect(action: "show", id: params.id)
			}
		}
		else {
			println "here3"
			flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'jobSite.label', default: 'JobSite'), params.id])}"
			//redirect(action: "list")
		}
	}
}

class JobSiteCommand {
	String id
	String pageUrl			//http://etc
	String name 			// caribbean jobs it
	String description = ''		// jobs for IT personel arround the caribbean
	String dataElement = ''
	String approvalStatus = ''

	static constraints = {
		id			(blank: true, nullable: true)
		pageUrl 	(url: true, blank: false, nullable: false, minSize: 'http://1.co'.size())
		dataElement (blank: true, nullable: false)
		name 		(blank: false, nullable: false, minSize: 3)
		description (blank: true, nullable: false)
		approvalStatus	(blank: true, nullable: false, inList: JobSiteApprovalStatusEnum.values()*.name())
	}
}